#include "wordfmt.h"
#include "gnumfmt.h"
#include <ctype.h>

WordFormat::WordFormat(char address, bool isSigned, char leading, char trailing)
: address_(address), isSigned_(isSigned), leading_(leading), trailing_(trailing)
{
}

WordFormat::WordFormat(const char *format)
{
	setFormatByStr(format);
}

int WordFormat::setFormatByStr(const char *format, unsigned char index /* = 0 */)
{
	enum {
		WF_START,
		WF_SET_ADDRESS,
		WF_SELECT_FORMAT,
		WF_IS_SIGNED,
		WF_SET_LEADING,
		WF_IS_FLOAT,
		WF_SET_TRAILING,
		WF_END_SELECT_FORMAT,
		WF_END,
		WF_ERROR
	} state = WF_START;
	char i = 0;
	unsigned char count;
	bool select;
	while ("format is being read")
		switch (state) {
		case WF_START:
			// entry
			select = false;
			// exit
			if (isalpha(format[i]))
				state = WF_SET_ADDRESS;
			else
				state = WF_ERROR;
			break;
		case WF_SET_ADDRESS:
			// entry
			address_ = format[i];
			// exit
			i++;
			if (format[i] == '+')
				state = WF_IS_SIGNED;
			else if (isdigit(format[i]))
				state = WF_SET_LEADING;
			else if (format[i] == '(')
				state = WF_SELECT_FORMAT;
			else
				state = WF_ERROR;
			break;
		case WF_SELECT_FORMAT:
			// entry
			if (format[i] == '(')
				count = 0;
			else if (format[i] == '|')
				count++;
			select = true;
			// exit
			i++;
			if (count == index) {
				if (format[i] == '+')
					state = WF_IS_SIGNED;
				else if (isdigit(format[i]))
					state = WF_SET_LEADING;
				else
					state = WF_ERROR;
			}
			break;
		case WF_IS_SIGNED:
			// entry
			isSigned_ = true;
			// exit
			i++;
			if (isdigit(format[i]))
				state = WF_SET_LEADING;
			else
				state = WF_ERROR;
			break;
		case WF_SET_LEADING:
			// entry
			leading_ = format[i] - '0';
			// exit
			i++;
			switch (format[i]) {
			case '.':
				state = WF_IS_FLOAT;
				break;
			case '|': case ')':
				if (true == select)
					state = WF_END_SELECT_FORMAT;
				break;
			case ' ': case '\0':
				if (false == select)
					state = WF_END;
				break;
			default:
				state = WF_ERROR;
			}
			break;
		case WF_IS_FLOAT:
			// exit
			i++;
			if (isdigit(format[i]))
				state = WF_SET_TRAILING;
			else
				state = WF_ERROR;
			break;
		case WF_SET_TRAILING:
			// entry
			trailing_ = format[i] - '0';
			// exit
			i++;
			switch (format[i]) {
			case '|': case ')':
				if (true == select)
					state = WF_END_SELECT_FORMAT;
				break;
			case ' ': case '\0':
				if (false == select)
					state = WF_END;
				break;
			default:
				state = WF_ERROR;
			}
			break;
		case WF_END_SELECT_FORMAT:
			// exit
			i++;
			if (format[i] == ' ' || format[i] == '\0')
				state = WF_END;
			else
				state = WF_ERROR;
			break;
		case WF_END:
			// exit
			i++;
			return i;
			break;
		case WF_ERROR:
			// entry
			return -1;
		}
	return i;
}

char WordFormat::getAddress() const
{
	return address_;
}

bool WordFormat::isSigned() const
{
	return isSigned_;
}

bool WordFormat::isFloat() const
{
	return trailing_ != 0;
}

char WordFormat::getLeading() const
{
	return leading_;
}

char WordFormat::getTrailing() const
{
	return trailing_;
}

bool WordFormat::check(Word word)
{
	GCodeNumberFormat number(word.getValue());
	if (word.getAddress() != address_)
		return false;
	if (number.isNegative() == true && isSigned_ == false)
		return false;
	if (number.isFloat() == true && isFloat() == false)
		return false;
	if (number.getLeading() > leading_)
		return false;
	if (number.getTrailing() > trailing_)
		return false;
	return true;
}
